﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Web.Script.Serialization;
using System.Linq;
using System.Xml.Linq;
using System.Text.RegularExpressions;
//
using PerceptiveMCAPI.Types.Internal;
//
namespace PerceptiveMCAPI.Types
{
   public class listMemberInfoOutput : Api_BaseOutput
   {
      public listMemberInfoResults result { get; set; }
      public listMemberInfoParms inputParms { get; private set; }
      // --------------------------------------------------------
      public listMemberInfoOutput( listMemberInfoInput input )
         : base( input )
      {
         inputParms = input.parms;
         result = new listMemberInfoResults();
      }
      // --------------------------------------------------- tostring
      public override string ToString()
      {
         return string.Format( "listMemberInfo - ToString TBD" );
      }

      #region ================================================================== format setup
      // ---- serial
      internal void Format( string api_Request, string api_Response )
      {
         this.api_Request = api_Request;
         this.api_Response = api_Response;
         // ----------------------------------
         if ( this.api_OutputType == EnumValues.OutputType.JSON )
            outputJSON();
         else if ( this.api_OutputType == EnumValues.OutputType.XML )
            outputXML();
      }
      // --------------------------------------------------------------------
      // ---- xmlrpc
      internal void Format( x_listMemberInfo rpcResult )
      {
         this.api_Request = EnumValues.NotSupplied;
         this.api_Response = EnumValues.NotSupplied;
         // ------------------------
         this.result = FormatResults( rpcResult );
      }
      #endregion

      #region ================================================================= Output
      // ---------------------------------------------------------- outputXML
      internal void outputXML()
      {
         XElement xml = XElement.Parse( this.api_Response );
         // error check
         Api_Error error = apiSerial.GetXmlOutputError( xml );
         if ( error != null )
         {
            this.api_ErrorMessages.Add( error );
            return;
         }
         // -------------------------------------------- get values
         try
         {
            DateTime dt; int it;
            XElement entry = xml;
            listMemberInfoResults mbrinfo = new listMemberInfoResults();
            //
            mbrinfo.id = entry.Element( "id" ).Value;
            mbrinfo.email = entry.Element( "email" ).Value;
            mbrinfo.email_type = apiHelper.GetEnumFromString<EnumValues.emailType>( entry.Element( "email_type" ).Value );
            mbrinfo.status = apiHelper.GetEnumFromString<EnumValues.listMembers_status>( entry.Element( "status" ).Value );
            mbrinfo.ip_opt = entry.Element( "ip_opt" ).Value;
            mbrinfo.ip_signup = entry.Element( "ip_signup" ).Value;
            if ( entry.Element( "member_rating" ) != null )
            {
               int.TryParse( entry.Element( "member_rating" ).Value, out it );
               mbrinfo.member_rating = it;
            }
            if ( entry.Element( "campaign_id" ) != null )
               mbrinfo.campaign_id = entry.Element( "campaign_id" ).Value;
            if ( entry.Element( "lists" ).HasElements )
            {
               IEnumerable<XElement> xlists = entry.Element( "lists" ).Elements();
               foreach ( XElement xl in xlists )
               {
                  EnumValues.listMembers_status xlsts = EnumValues.listMembers_status.NotSpecified;
                  string xlname = "";
                  // -- happens if numeric listid -- bad xml parser
                  if ( xl.Name == "struct" )
                     xlname = xl.Attribute( "key" ).Value ?? "* missing *";
                  else
                     xlname = xl.Name.LocalName;
                  if ( !string.IsNullOrEmpty( xl.Value ) )
                     xlsts = apiHelper.GetEnumFromString<EnumValues.listMembers_status>( xl.Value );
                  mbrinfo.lists.Add( xlname, xlsts );
               }
            }
            DateTime.TryParse( entry.Element( "timestamp" ).Value, out dt );
            mbrinfo.timestamp = dt;
            DateTime.TryParse( entry.Element( "info_changed" ).Value, out dt );
            mbrinfo.info_changed = dt;
            int.TryParse( entry.Element( "web_id" ).Value, out it );
            mbrinfo.web_id = it;
            //
            if ( entry.Element( "merges" ).HasElements )
            {
               IEnumerable<XElement> merge_vars = entry.Element( "merges" ).Elements();
               foreach ( XElement mv in merge_vars )
               {
                  if ( mv.Name.LocalName.ToLower() == "groupings" )
                  {
                     List<interestGroupings> groupings = new List<interestGroupings>();
                     IEnumerable<XElement> grouping_elem = mv.Elements( "struct" );
                     foreach ( XElement elem in grouping_elem )
                     {
                        interestGroupings grouping = new interestGroupings();
                        int.TryParse( elem.Element( "id" ).Value, out it );
                        grouping.id = it;
                        grouping.name = elem.Element( "name" ).Value;
                        //
                        // -- this didn't work when commas are in the groups; codeplex issue #5381
                        // grouping.groups = apiHelper.MakeListFromString( elem.Element( "groups" ).Value );
                        // -- thanks go out to 'checksix' for the below regex solution
                        //
                        grouping.groups = Regex.Split( elem.Element( "groups" ).Value, @"(?<=[^\\])," ).Select( R => R.Replace( @"\,", "," ).Trim() ).ToList();

                        groupings.Add( grouping );
                     }
                     mbrinfo.groupings = groupings;
                  }
                  else if ( mv.HasElements )
                  {
                     IEnumerable<XElement> mv_array = mv.Elements();
                     Dictionary<string, object> dict = new Dictionary<string, object>();
                     foreach ( XElement mva in mv_array )
                     {
                        dict.Add( mva.Name.LocalName, mva.Value );
                     }
                     mbrinfo.merges.Add( mv.Name.LocalName, dict );
                  }
                  else
                     mbrinfo.merges.Add( mv.Name.LocalName, mv.Value );
               }
            }

            this.result = mbrinfo;
         }
         catch ( Exception ex )
         {
            this.api_ErrorMessages.Add( apiException.FormatError( ex ) );
         }
      }

      // ---------------------------------------------------------- outputJSON
      internal void outputJSON()
      {
         JavaScriptSerializer jss = new JavaScriptSerializer();

         try
         {
            if ( this.api_Response.Contains( EnumValues.JSONErrorString ) )
            {
               Api_Error err = jss.Deserialize<Api_Error>( this.api_Response );
               this.api_ErrorMessages.Add( err );
            }
            else
            {
               x_listMemberInfo jsonResult = jss.Deserialize<x_listMemberInfo>( this.api_Response );
               this.result = FormatResults( jsonResult );
            }
         }
         catch ( Exception ex )
         {
            this.api_ErrorMessages.Add( apiException.FormatError( ex ) );
         }
      }

      // ---------------------------------------------------------- FormatResults
      private listMemberInfoResults FormatResults( x_listMemberInfo x_results )
      {
         DateTime dt;
         listMemberInfoResults results = new listMemberInfoResults();
         results.id = x_results.id;
         results.email = x_results.email;
         results.email_type = apiHelper.GetEnumFromString<EnumValues.emailType>( x_results.email_type );
         results.status = apiHelper.GetEnumFromString<EnumValues.listMembers_status>( x_results.status );
         results.ip_opt = x_results.ip_opt;
         results.ip_signup = x_results.ip_signup;
         results.member_rating = x_results.member_rating;
         if ( x_results.campaign_id != null ) results.campaign_id = x_results.campaign_id;

         DateTime.TryParse( x_results.timestamp, out dt );
         results.timestamp = dt;
         DateTime.TryParse( x_results.info_changed, out dt );
         results.info_changed = dt;
         results.web_id = x_results.web_id;
         // -------------------------------------------------------- do merge & list converts
         Dictionary<string, object> merge_vars;
         List<interestGroupings> groupings;
         FormatMerges( x_results.merges, out merge_vars, out groupings );      // mergevars and groupings split out
         results.merges = merge_vars;
         results.groupings = groupings;
         results.lists = FormatLists( x_results.lists );
         //------------
         return results;
      }
      //
      private void FormatMerges( object mergeObject,
         out Dictionary<string, object> merge_vars,
         out List<interestGroupings> groupings )
      {
         merge_vars = new Dictionary<string, object>();
         groupings = new List<interestGroupings>();
         int it;
         // object is array that is empty if no merge variables
         if ( mergeObject == null || mergeObject.GetType().IsArray ) return;

         IDictionary merges = mergeObject as IDictionary;
         IDictionaryEnumerator de = merges.GetEnumerator();
         while ( de.MoveNext() )
         {
            if ( de.Key.ToString().ToLower() == "groupings" )
            {
               object[] groupings_obj = de.Value as object[];
               foreach ( object grouping_obj in groupings_obj )
               {
                  interestGroupings grouping = new interestGroupings();
                  IDictionary grouping_dict = grouping_obj as IDictionary;
                  int.TryParse( grouping_dict["id"].ToString(), out it );
                  grouping.id = it;
                  grouping.name = grouping_dict["name"].ToString();
                  string groups_string = grouping_dict["groups"].ToString();
                  //
                  // -- this didn't work when commas are in the groups; codeplex issue #5381
                  // grouping.groups = apiHelper.MakeListFromString( groups_string ); 
                  // -- thanks go out to 'checksix' for the below regex solution
                  //
                  grouping.groups = Regex.Split( groups_string, @"(?<=[^\\])," ).Select( R => R.Replace( @"\,", "," ).Trim() ).ToList();
                  //
                  groupings.Add( grouping );
               }
            }
            else if ( de.Value.GetType().Name.ToLower().Contains( "dictionary" )
                || de.Value.GetType().Name.ToLower().Contains( "xmlrpcstruct" ) )
            {
               IDictionary varDict = de.Value as IDictionary;
               IDictionaryEnumerator vd = varDict.GetEnumerator();
               Dictionary<string, object> newDict = new Dictionary<string, object>();
               while ( vd.MoveNext() )
               {
                  newDict.Add( vd.Key.ToString(), vd.Value.ToString() );
               }
               merge_vars.Add( de.Key.ToString(), newDict );
            }
            else
               merge_vars.Add( de.Key.ToString(), de.Value );
         }
      }
      //
      private Dictionary<string, EnumValues.listMembers_status> FormatLists( object listObject )
      {
         Dictionary<string, EnumValues.listMembers_status> lists = new Dictionary<string, EnumValues.listMembers_status>();

         // object is array that is empty if no merge variables
         if ( listObject == null || listObject.GetType().IsArray ) return lists;
         //
         IDictionary list = listObject as IDictionary;
         IDictionaryEnumerator le = list.GetEnumerator();

         while ( le.MoveNext() )
         {
            // MC passes back bad data (null) in xml-rpc when list id is numeric -- check for it
            // hopefully they will be able fix this problem
            string lid = "";
            if ( le.Key == null || string.IsNullOrEmpty( le.Key.ToString() ) )
               lid = "* missing *";
            else lid = le.Key.ToString();

            lists.Add( lid,
               apiHelper.GetEnumFromString<EnumValues.listMembers_status>( le.Value.ToString() ) );
         }

         // -----------------------
         return lists;
      }

      #endregion
   }
}
